package com.net.framework.help.widget;

import android.content.Context;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

public class OverScrollView extends ScrollView {

	/**
	 * ����ϵ��, ��ͼ������������ָ��������ı�ֵ
	 */
	private static final float ELASTICITY_COEFFICIENT = 0.25f;

	/**
	 * �޵��Թ���״̬
	 */
	private static final int NO_OVERSCROLL_STATE = 0;

	/**
	 * �Ϸ����Թ���״̬
	 */
	private static final int TOP_OVERSCROLL_STATE = 1;

	/**
	 * �·����Թ���״̬
	 */
	private static final int BOTTOM_OVERSCROLL_STATE = 2;

	/**
	 * �������߶�,����˸߶Ȳ��ٹ���
	 */
	private static final int OVERSCROLL_MAX_HEIGHT = 1200;

	/**
	 * Sentinel value for no current active pointer. Used by
	 * {@link #mActivePointerId}.
	 */
	private static final int INVALID_POINTER = -1;

	/**
	 * �����¼��ĸ߶�Ĭ�Ϸ�ֵ
	 */
	private static final int TRIGGER_HEIGHT = 120;

	/**
	 * ���Թ���״̬
	 */
	private int overScrollSate;

	/**
	 * ���Ա�־λ:�Ƿ���Ե��Թ���
	 */
	private boolean mIsUseOverScroll = true;

	/**
	 * �Ƿ��ǿ��Թ���
	 */
	private boolean isRecord;

	/**
	 * �Զ���ĵ��Թ�����ͼ
	 */
	private OverScrollWarpLayout mContentLayout;

	/**
	 * OverScroll������
	 */
	private OverScrollListener mOverScrollListener;

	/**
	 * OverScrollϸ�¼�����
	 */
	private OverScrollTinyListener mOverScrollTinyListener;

	/**
	 * Scrollϸ�¼�����
	 */
	private OnScrollListener mScrollListener;

	/**
	 * ����һ�ε���ָ����λ��
	 */
	private float mLastMotionY;

	/**
	 * ���Թ����ܾ���,����Ϊ��������Ϊ��
	 */
	private int overScrollDistance;

	/**
	 * ������Ļ�ϵ���ָ��id
	 */
	private int mActivePointerId = INVALID_POINTER;

	/**
	 * �Ƿ���
	 */
	private boolean isOnTouch;

	/**
	 * �Ƿ���й���
	 */
	private boolean isInertance;

	/**
	 * �Ƿ�ʹ�ù���
	 */
	private boolean mIsUseInertance = true;

	/**
	 * �Ƿ���ÿ��ٹ���
	 */
	private boolean mIsBanQuickScroll;

	/**
	 * ���Ծ���(�뻬�������й�)
	 */
	private int inertanceY;

	/**
	 * �����¼��ĸ߶ȷ�ֵ����СֵΪ30
	 */
	private int mOverScrollTrigger = TRIGGER_HEIGHT;

	public OverScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initScrollView();
	}

	public OverScrollView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public OverScrollView(Context context) {
		this(context, null);
	}

	private void initScrollView() {
		// ���ù�������Ӱ( API Level 9 )
		if (Build.VERSION.SDK_INT >= 9) {
			setOverScrollMode(View.OVER_SCROLL_NEVER);
		} else {
			ViewCompat.setOverScrollMode(this, ViewCompat.OVER_SCROLL_NEVER);
		}
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// �����ã������κδ���
		if (!mIsUseOverScroll) {
			return super.onInterceptTouchEvent(ev);
		}
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (isOverScrolled()) {
				isRecord = true;
				// Remember where the motion event started
				mLastMotionY = (int) ev.getY();

				mActivePointerId = ev.getPointerId(0);
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (isRecord && Math.abs(ev.getY() - mLastMotionY) > 20) {
				return true;
			}
			break;
		case MotionEvent.ACTION_CANCEL:
			if (isRecord) {
				isRecord = false;
			}
		}
		return super.onInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		isOnTouch = true;
		if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
			if (mOverScrollTinyListener != null) {
				mOverScrollTinyListener.scrollLoosen();
			}
			isOnTouch = false;
		}

		// �����ã������κδ���
		if (!mIsUseOverScroll) {
			return super.onTouchEvent(ev);
		}

		if (!isOverScrolled()) {
			mLastMotionY = (int) ev.getY();
			return super.onTouchEvent(ev);
		}

		switch (ev.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			mActivePointerId = ev.getPointerId(0);
			mLastMotionY = (int) ev.getY();
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			final int index = ev.getActionIndex();
			mLastMotionY = (int) ev.getY(index);
			mActivePointerId = ev.getPointerId(index);
			break;
		case MotionEvent.ACTION_POINTER_UP:
			onSecondaryPointerUp(ev);
			if (mActivePointerId != INVALID_POINTER) {
				mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (isRecord) {
				final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
				if (activePointerIndex == -1) {
					break;
				}

				final float y = ev.getY(activePointerIndex);
				// ��������
				int deltaY = (int) (mLastMotionY - y);
				// ��¼�µĴ���λ��
				mLastMotionY = y;

				if (Math.abs(overScrollDistance) >= OVERSCROLL_MAX_HEIGHT && overScrollDistance * deltaY > 0) {
					deltaY = 0;
				}

				// ��������ScrollView��������߽磬ֱ�ӵ����������
				if (overScrollDistance * (overScrollDistance + deltaY) < 0) {
					mContentLayout.smoothScrollToNormal();
					overScrollDistance = 0;
					break;
				}

				// �����ScrollView����״̬��ֱ�ӵ���ScrollView�������
				if ((!isOnBottom() && overScrollDistance > 0) || (!isOnTop() && overScrollDistance < 0)) {
					mContentLayout.smoothScrollToNormal();
					overScrollDistance = 0;
					break;
				}

				if (overScrollDistance * deltaY > 0) {
					deltaY = (int) (deltaY * ELASTICITY_COEFFICIENT);
				}

				if (overScrollDistance == 0) {
					deltaY = (int) (deltaY * ELASTICITY_COEFFICIENT * 0.5f);
				}

				if (overScrollDistance == 0 && deltaY == 0) {
					break;
				}

				// ������չ������룬���Ϊ20
				if (Math.abs(deltaY) > 20) {
					deltaY = deltaY > 0 ? 20 : -20;
				}

				// ��¼�����ܾ���
				overScrollDistance += deltaY;

				if (isOnTop() && overScrollDistance > 0 && !isOnBottom()) {
					overScrollDistance = 0;
					break;
				}

				if (isOnBottom() && overScrollDistance < 0 && !isOnTop()) {
					overScrollDistance = 0;
					break;
				}

				// ������ͼ
				mContentLayout.smoothScrollBy(0, deltaY);

				if (mOverScrollTinyListener != null) {
					mOverScrollTinyListener.scrollDistance(deltaY, overScrollDistance);
				}
				return true;
			}
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			mContentLayout.smoothScrollToNormal();
			overScrollTrigger();
			// ���û����ܾ���
			overScrollDistance = 0;
			// ���ñ��
			isRecord = false;
			// ������ָ����id
			mActivePointerId = INVALID_POINTER;
			break;

		default:
			break;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * ��������: ��ֹ����pointerIndex out of range�쳣<br>
	 * 
	 * @param ev
	 */
	private void onSecondaryPointerUp(MotionEvent ev) {
		final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
		final int pointerId = ev.getPointerId(pointerIndex);
		if (pointerId == mActivePointerId) {
			// This was our active pointer going up. Choose a new
			// active pointer and adjust accordingly.
			// TODO: Make this decision more intelligent.
			final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
			mLastMotionY = (int) ev.getY(newPointerIndex);
			mActivePointerId = ev.getPointerId(newPointerIndex);
		}

	}

	public boolean isOverScrolled() {
		return isOnTop() || isOnBottom();
	}

	private boolean isOnTop() {
		return getScrollY() == 0;
	}

	private boolean isOnBottom() {
		return getScrollY() + getHeight() == mContentLayout.getHeight();
	}

	/**
	 * ��������:��ʼ��������ͼ <br>
	 * <br>
	 * <strong>����:</strong> ��ȡScrollView������ͼ����ӵ��Զ����
	 * {@link OverScrollWarpLayout}������ͼ�� ��������ͼ��ӵ�ScrollView��Ϊ����ͼ
	 */
	private void initOverScrollLayout() {
		// ��������Ϊtrue,�����������ͼʱ�߶Ȳ�����䵽���ScrollView�ĸ߶�
		setFillViewport(true);
		if (mContentLayout == null) {
			// ��ȡScrollView������ͼ
			View child = getChildAt(0);
			// ��ʼ�����Թ�����ͼ
			mContentLayout = new OverScrollWarpLayout(getContext());
			// �Ƴ�ScrollView������ͼ
			this.removeAllViews();
			// ��ԭ��ScrollView����ͼ���뵽���Թ�����ͼ��
			mContentLayout.addView(child);
			// ��ӵ��Թ�����ͼ����ΪScrollView����ͼ
			this.addView(mContentLayout, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

		}
		// mIsUseOverScroll = true;
	}

	/**
	 * ��������:�����Ƿ���Ե��Թ��� <br>
	 * 
	 * @param isOverScroll
	 */
	public void setOverScroll(boolean isOverScroll) {
		mIsUseOverScroll = isOverScroll;
	}

	/**
	 * ��������: �����Ƿ�ʹ�ù���<br>
	 * 
	 * @param isInertance
	 */
	public void setUseInertance(boolean isInertance) {
		mIsUseInertance = isInertance;
	}

	@Override
	protected void onAttachedToWindow() {
		initOverScrollLayout();
		super.onAttachedToWindow();
	}

	/**
	 * ��������: ��ȡ����״̬<br>
	 * 
	 * @return
	 */
	public int getScrollState() {
		invalidateState();
		return overScrollSate;
	}

	/**
	 * ��������: ˢ�µ��Թ���״̬<br>
	 */
	private void invalidateState() {

		if (mContentLayout.getScrollerCurrY() == 0) {
			overScrollSate = NO_OVERSCROLL_STATE;
		}

		if (mContentLayout.getScrollerCurrY() < 0) {
			overScrollSate = TOP_OVERSCROLL_STATE;
		}

		if (mContentLayout.getScrollerCurrY() > 0) {
			overScrollSate = BOTTOM_OVERSCROLL_STATE;
		}
	}

	@Override
	protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
		// Log.v("test", "deltaY "+deltaY+"   scrollY "+scrollY);
		return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		if (mScrollListener != null && overScrollDistance == 0) {
			mScrollListener.onScroll(l, t, oldl, oldt);
		}
		super.onScrollChanged(l, t, oldl, oldt);
	}

	@Override
	protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
		if (mIsUseInertance && !isInertance && scrollY != 0) {
			isInertance = true;
		}
		if (clampedY && !isOnTouch && isInertance) {
			mContentLayout.smoothScrollBy(0, inertanceY);
			mContentLayout.smoothScrollToNormal();
			inertanceY = 0;
		}
		super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
	}

	/**
	 * ��������: ����OverScroll����������<br>
	 * 
	 * @param listener
	 */
	public void setOverScrollListener(OverScrollListener listener) {
		mOverScrollListener = listener;
	}

	/**
	 * ��������: ����OverScroll����������<br>
	 * 
	 * @param listener
	 */
	public void setOverScrollTinyListener(OverScrollTinyListener listener) {
		mOverScrollTinyListener = listener;
	}

	/**
	 * ��������: ����Scroll����������<br>
	 * 
	 * @param listener
	 */
	public void setOnScrollListener(OnScrollListener listener) {
		mScrollListener = listener;
	}

	/**
	 * ����OverScrollListener������ֵ
	 * 
	 * @param height
	 */
	public void setOverScrollTrigger(int height) {
		if (height >= 30) {
			mOverScrollTrigger = height;
		}
	}

	private void overScrollTrigger() {
		if (mOverScrollListener == null) {
			return;
		}

		if (overScrollDistance > mOverScrollTrigger && isOnBottom()) {
			mOverScrollListener.footerScroll();
		}

		if (overScrollDistance < -mOverScrollTrigger && isOnTop()) {
			mOverScrollListener.headerScroll();
		}

	}

	public void setQuickScroll(boolean isEnable) {
		mIsBanQuickScroll = !isEnable;
	}

	@Override
	public void computeScroll() {
		if (!mIsBanQuickScroll) {
			super.computeScroll();
		}
	}

	/**
	 * ��ȡScrollView�ɹ����߶�
	 * 
	 * @return
	 */
	public int getScrollHeight() {
		return mContentLayout.getHeight() - getHeight();
	}

	@Override
	public void fling(int velocityY) {
		inertanceY = 50 * velocityY / 5000;
		super.fling(velocityY);
	}

	/**
	 * ��OverScroll����һ��ֵʱ�����ô˼���
	 * 
	 * @author King
	 * @since 2014-4-9 ����4:36:29
	 */
	public interface OverScrollListener {

		/**
		 * ����
		 */
		void headerScroll();

		/**
		 * �ײ�
		 */
		void footerScroll();

	}

	/**
	 * ÿ��OverScrollʱ�����ܴ����ļ���
	 * 
	 * @author King
	 * @since 2014-4-9 ����4:39:06
	 */
	public interface OverScrollTinyListener {

		/**
		 * ��������
		 * 
		 * @param tinyDistance
		 *            ��ǰ������ϸС����
		 * @param totalDistance
		 *            �������ܾ���
		 */
		void scrollDistance(int tinyDistance, int totalDistance);

		/**
		 * �����ɿ�
		 */
		void scrollLoosen();
	}

	/**
	 * ��ͨ����������<br>
	 * overScroll����Ϊ0���޹���ʱ����
	 * 
	 * @author king
	 * 
	 */
	public interface OnScrollListener {
		void onScroll(int l, int t, int oldl, int oldt);
	}
}
